/* BEGIN_HEADER */
#include "psa/crypto_se_driver.h"

#include "psa_crypto_se.h"
#include "psa_crypto_slot_management.h"
#include "psa_crypto_storage.h"

/* Invasive peeking: check the persistent data */
#if defined(MBEDTLS_PSA_ITS_FILE_C)
#include "psa_crypto_its.h"
#else /* Native ITS implementation */
#include "psa/error.h"
#include "psa/internal_trusted_storage.h"
#endif


/****************************************************************/
/* Test driver helpers */
/****************************************************************/

/** The minimum valid location value for a secure element driver. */
#define MIN_DRIVER_LOCATION 1

/** The location and lifetime used for tests that use a single driver. */
#define TEST_DRIVER_LOCATION 1
#define TEST_SE_PERSISTENT_LIFETIME                             \
    (PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(           \
         PSA_KEY_PERSISTENCE_DEFAULT, TEST_DRIVER_LOCATION))

#define TEST_SE_VOLATILE_LIFETIME                               \
    (PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(           \
         PSA_KEY_PERSISTENCE_VOLATILE, TEST_DRIVER_LOCATION))

/** The driver detected a condition that shouldn't happen.
 * This is probably a bug in the library. */
#define PSA_ERROR_DETECTED_BY_DRIVER ((psa_status_t) (-500))

/** Like #TEST_ASSERT for use in a driver method, with no cleanup.
 *
 * If an error happens, this macro returns from the calling function.
 *
 * Use this macro to assert on guarantees provided by the core.
 */
#define DRIVER_ASSERT_RETURN(TEST)                        \
    do {                                                    \
        if (!(TEST))                                       \
        {                                                    \
            mbedtls_test_fail( #TEST, __LINE__, __FILE__);   \
            return PSA_ERROR_DETECTED_BY_DRIVER;           \
        }                                                    \
    } while (0)

/** Like #TEST_ASSERT for use in a driver method, with cleanup.
 *
 * In case of error, this macro sets `status` and jumps to the
 * label `exit`.
 *
 * Use this macro to assert on guarantees provided by the core.
 */
#define DRIVER_ASSERT(TEST)                               \
    do {                                                    \
        if (!(TEST))                                       \
        {                                                    \
            mbedtls_test_fail( #TEST, __LINE__, __FILE__);   \
            status = PSA_ERROR_DETECTED_BY_DRIVER;            \
            goto exit;                                        \
        }                                                    \
    } while (0)

/** Like #PSA_ASSERT for a PSA API call that calls a driver underneath.
 *
 * Run the code \p expr. If this returns \p expected_status,
 * do nothing. If this returns #PSA_ERROR_DETECTED_BY_DRIVER,
 * jump directly to the `exit` label. If this returns any other
 * status, call mbedtls_test_fail() then jump to `exit`.
 *
 * The special case for #PSA_ERROR_DETECTED_BY_DRIVER is because in this
 * case, the test driver code is expected to have called mbedtls_test_fail()
 * already, so we make sure not to overwrite the failure information.
 */
#define PSA_ASSERT_VIA_DRIVER(expr, expected_status)                  \
    do {                                                                \
        psa_status_t PSA_ASSERT_VIA_DRIVER_status = (expr);           \
        if (PSA_ASSERT_VIA_DRIVER_status == PSA_ERROR_DETECTED_BY_DRIVER) \
        goto exit;                                                  \
        if (PSA_ASSERT_VIA_DRIVER_status != (expected_status))       \
        {                                                               \
            mbedtls_test_fail( #expr, __LINE__, __FILE__);                     \
            goto exit;                                                  \
        }                                                               \
    } while (0)



/****************************************************************/
/* Domain support functions */
/****************************************************************/

/* Return the exact bit size given a curve family and a byte length. */
static size_t ecc_curve_bits(psa_ecc_family_t curve, size_t data_length)
{
    switch (curve) {
        case PSA_ECC_FAMILY_SECP_R1:
            if (data_length == PSA_BYTES_TO_BITS(521)) {
                return 521;
            }
            break;
        case PSA_ECC_FAMILY_MONTGOMERY:
            if (data_length == PSA_BYTES_TO_BITS(255)) {
                return 255;
            }
    }
    /* If not listed above, assume a multiple of 8 bits. */
    return PSA_BYTES_TO_BITS(data_length);
}


/****************************************************************/
/* Miscellaneous driver methods */
/****************************************************************/

typedef struct {
    psa_key_slot_number_t slot_number;
    psa_key_creation_method_t method;
    psa_status_t status;
} validate_slot_number_directions_t;
static validate_slot_number_directions_t validate_slot_number_directions;

/* Validate a choice of slot number as directed. */
static psa_status_t validate_slot_number_as_directed(
    psa_drv_se_context_t *context,
    void *persistent_data,
    const psa_key_attributes_t *attributes,
    psa_key_creation_method_t method,
    psa_key_slot_number_t slot_number)
{
    (void) context;
    (void) persistent_data;
    (void) attributes;
    DRIVER_ASSERT_RETURN(slot_number ==
                         validate_slot_number_directions.slot_number);
    DRIVER_ASSERT_RETURN(method ==
                         validate_slot_number_directions.method);
    return validate_slot_number_directions.status;
}

/* Allocate slot numbers with a monotonic counter. */
static psa_key_slot_number_t shadow_counter;
static void counter_reset(void)
{
    shadow_counter = 0;
}
static psa_status_t counter_allocate(psa_drv_se_context_t *context,
                                     void *persistent_data,
                                     const psa_key_attributes_t *attributes,
                                     psa_key_creation_method_t method,
                                     psa_key_slot_number_t *slot_number)
{
    psa_key_slot_number_t *p_counter = persistent_data;
    (void) attributes;
    (void) method;
    if (context->persistent_data_size != sizeof(psa_key_slot_number_t)) {
        return PSA_ERROR_DETECTED_BY_DRIVER;
    }
    ++*p_counter;
    if (*p_counter == 0) {
        return PSA_ERROR_INSUFFICIENT_STORAGE;
    }
    shadow_counter = *p_counter;
    *slot_number = *p_counter;
    return PSA_SUCCESS;
}

/* Null import: do nothing, but pretend it worked. */
#if defined(AT_LEAST_ONE_BUILTIN_KDF)
static psa_status_t null_import(psa_drv_se_context_t *context,
                                psa_key_slot_number_t slot_number,
                                const psa_key_attributes_t *attributes,
                                const uint8_t *data,
                                size_t data_length,
                                size_t *bits)
{
    (void) context;
    (void) slot_number;
    (void) attributes;
    (void) data;
    /* We're supposed to return a key size. Return one that's correct for
     * plain data keys. */
    *bits = PSA_BYTES_TO_BITS(data_length);
    return PSA_SUCCESS;
}
#endif /* AT_LEAST_ONE_BUILTIN_KDF */

/* Null generate: do nothing, but pretend it worked. */
#if defined(AT_LEAST_ONE_BUILTIN_KDF)
static psa_status_t null_generate(psa_drv_se_context_t *context,
                                  psa_key_slot_number_t slot_number,
                                  const psa_key_attributes_t *attributes,
                                  uint8_t *pubkey,
                                  size_t pubkey_size,
                                  size_t *pubkey_length)
{
    (void) context;
    (void) slot_number;
    (void) attributes;

    DRIVER_ASSERT_RETURN(*pubkey_length == 0);
    if (!PSA_KEY_TYPE_IS_KEY_PAIR(psa_get_key_type(attributes))) {
        DRIVER_ASSERT_RETURN(pubkey == NULL);
        DRIVER_ASSERT_RETURN(pubkey_size == 0);
    }

    return PSA_SUCCESS;
}
#endif /* AT_LEAST_ONE_BUILTIN_KDF */

/* Null destroy: do nothing, but pretend it worked. */
static psa_status_t null_destroy(psa_drv_se_context_t *context,
                                 void *persistent_data,
                                 psa_key_slot_number_t slot_number)
{
    (void) context;
    (void) persistent_data;
    (void) slot_number;
    return PSA_SUCCESS;
}



/****************************************************************/
/* RAM-based test driver */
/****************************************************************/

#define RAM_MAX_KEY_SIZE 64
typedef struct {
    psa_key_lifetime_t lifetime;
    psa_key_type_t type;
    size_t bits;
    uint8_t content[RAM_MAX_KEY_SIZE];
} ram_slot_t;
static ram_slot_t ram_slots[16];

/* A type with at least ARRAY_LENGTH(ram_slots) bits, containing a
 * bit vector indicating which slots are in use. */
typedef uint16_t ram_slot_usage_t;

static ram_slot_usage_t ram_shadow_slot_usage;

static uint8_t ram_min_slot = 0;

static void ram_slots_reset(void)
{
    memset(ram_slots, 0, sizeof(ram_slots));
    ram_min_slot = 0;
    ram_shadow_slot_usage = 0;
}

/* Common parts of key creation.
 *
 * In case of error, zero out ram_slots[slot_number]. But don't
 * do that if the error is PSA_ERROR_DETECTED_BY_DRIVER: in this case
 * you don't need to clean up (ram_slot_reset() will take care of it
 * in the test case function's cleanup code) and it might be wrong
 * (if slot_number is invalid).
 */
static psa_status_t ram_create_common(psa_drv_se_context_t *context,
                                      psa_key_slot_number_t slot_number,
                                      const psa_key_attributes_t *attributes,
                                      size_t required_storage)
{
    (void) context;
    DRIVER_ASSERT_RETURN(slot_number < ARRAY_LENGTH(ram_slots));

    ram_slots[slot_number].lifetime = psa_get_key_lifetime(attributes);
    ram_slots[slot_number].type = psa_get_key_type(attributes);
    ram_slots[slot_number].bits = psa_get_key_bits(attributes);

    if (required_storage > sizeof(ram_slots[slot_number].content)) {
        memset(&ram_slots[slot_number], 0, sizeof(ram_slots[slot_number]));
        return PSA_ERROR_INSUFFICIENT_STORAGE;
    }

    return PSA_SUCCESS;
}

/* This function does everything except actually generating key material.
 * After calling it, you must copy the desired key material to
 * ram_slots[slot_number].content. */
static psa_status_t ram_fake_generate(psa_drv_se_context_t *context,
                                      psa_key_slot_number_t slot_number,
                                      const psa_key_attributes_t *attributes,
                                      uint8_t *pubkey,
                                      size_t pubkey_size,
                                      size_t *pubkey_length)
{
    psa_status_t status;
    size_t required_storage =
        PSA_EXPORT_KEY_OUTPUT_SIZE(psa_get_key_type(attributes),
                                   psa_get_key_bits(attributes));

    DRIVER_ASSERT_RETURN(*pubkey_length == 0);
    if (!PSA_KEY_TYPE_IS_KEY_PAIR(psa_get_key_type(attributes))) {
        DRIVER_ASSERT_RETURN(pubkey == NULL);
        DRIVER_ASSERT_RETURN(pubkey_size == 0);
    }

    status = ram_create_common(context, slot_number, attributes,
                               required_storage);
    return status;
}

static psa_status_t ram_import(psa_drv_se_context_t *context,
                               psa_key_slot_number_t slot_number,
                               const psa_key_attributes_t *attributes,
                               const uint8_t *data,
                               size_t data_length,
                               size_t *bits)
{
    psa_key_type_t type = psa_get_key_type(attributes);
    psa_status_t status = ram_create_common(context, slot_number, attributes,
                                            data_length);
    if (status != PSA_SUCCESS) {
        return status;
    }

    /* The RAM driver only works for certain key types: raw keys,
     * and ECC key pairs. This is true in particular of the bit-size
     * calculation here. */
    if (PSA_KEY_TYPE_IS_UNSTRUCTURED(type)) {
        *bits = PSA_BYTES_TO_BITS(data_length);
    } else if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(type)) {
        *bits = ecc_curve_bits(PSA_KEY_TYPE_ECC_GET_FAMILY(type), data_length);
        if (*bits == 0) {
            return PSA_ERROR_DETECTED_BY_DRIVER;
        }
    } else {
        memset(&ram_slots[slot_number], 0, sizeof(ram_slots[slot_number]));
        return PSA_ERROR_NOT_SUPPORTED;
    }

    ram_slots[slot_number].bits = *bits;
    memcpy(ram_slots[slot_number].content, data, data_length);

    return PSA_SUCCESS;
}

static psa_status_t ram_export(psa_drv_se_context_t *context,
                               psa_key_slot_number_t slot_number,
                               uint8_t *data,
                               size_t data_size,
                               size_t *data_length)
{
    size_t actual_size;
    (void) context;
    DRIVER_ASSERT_RETURN(slot_number < ARRAY_LENGTH(ram_slots));
    actual_size = PSA_BITS_TO_BYTES(ram_slots[slot_number].bits);
    if (actual_size > data_size) {
        return PSA_ERROR_BUFFER_TOO_SMALL;
    }
    *data_length = actual_size;
    memcpy(data, ram_slots[slot_number].content, actual_size);
    return PSA_SUCCESS;
}

static psa_status_t ram_export_public(psa_drv_se_context_t *context,
                                      psa_key_slot_number_t slot_number,
                                      uint8_t *data,
                                      size_t data_size,
                                      size_t *data_length)
{
    psa_status_t status;
    mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;

    (void) context;
    DRIVER_ASSERT_RETURN(slot_number < ARRAY_LENGTH(ram_slots));
    DRIVER_ASSERT_RETURN(
        PSA_KEY_TYPE_IS_KEY_PAIR(ram_slots[slot_number].type));

    psa_set_key_type(&attributes, ram_slots[slot_number].type);
    status = psa_import_key(&attributes,
                            ram_slots[slot_number].content,
                            PSA_BITS_TO_BYTES(ram_slots[slot_number].bits),
                            &key);
    if (status != PSA_SUCCESS) {
        return status;
    }
    status = psa_export_public_key(key, data, data_size, data_length);
    psa_destroy_key(key);
    return PSA_SUCCESS;
}

static psa_status_t ram_destroy(psa_drv_se_context_t *context,
                                void *persistent_data,
                                psa_key_slot_number_t slot_number)
{
    ram_slot_usage_t *slot_usage = persistent_data;
    DRIVER_ASSERT_RETURN(context->persistent_data_size == sizeof(ram_slot_usage_t));
    DRIVER_ASSERT_RETURN(slot_number < ARRAY_LENGTH(ram_slots));
    memset(&ram_slots[slot_number], 0, sizeof(ram_slots[slot_number]));
    *slot_usage &= ~(ram_slot_usage_t) (1 << slot_number);
    ram_shadow_slot_usage = *slot_usage;
    return PSA_SUCCESS;
}

static psa_status_t ram_allocate(psa_drv_se_context_t *context,
                                 void *persistent_data,
                                 const psa_key_attributes_t *attributes,
                                 psa_key_creation_method_t method,
                                 psa_key_slot_number_t *slot_number)
{
    ram_slot_usage_t *slot_usage = persistent_data;
    (void) attributes;
    (void) method;
    DRIVER_ASSERT_RETURN(context->persistent_data_size == sizeof(ram_slot_usage_t));
    for (*slot_number = ram_min_slot;
         *slot_number < ARRAY_LENGTH(ram_slots);
         ++(*slot_number)) {
        if (!(*slot_usage & 1 << *slot_number)) {
            ram_shadow_slot_usage = *slot_usage;
            return PSA_SUCCESS;
        }
    }
    return PSA_ERROR_INSUFFICIENT_STORAGE;
}

static psa_status_t ram_validate_slot_number(
    psa_drv_se_context_t *context,
    void *persistent_data,
    const psa_key_attributes_t *attributes,
    psa_key_creation_method_t method,
    psa_key_slot_number_t slot_number)
{
    (void) context;
    (void) persistent_data;
    (void) attributes;
    (void) method;
    if (slot_number >= ARRAY_LENGTH(ram_slots)) {
        return PSA_ERROR_INVALID_ARGUMENT;
    }
    return PSA_SUCCESS;
}

static psa_status_t ram_sign(psa_drv_se_context_t *context,
                             psa_key_slot_number_t slot_number,
                             psa_algorithm_t alg,
                             const uint8_t *hash,
                             size_t hash_length,
                             uint8_t *signature,
                             size_t signature_size,
                             size_t *signature_length)
{
    ram_slot_t *slot;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT;
    psa_status_t status = PSA_ERROR_GENERIC_ERROR;

    (void) context;
    DRIVER_ASSERT_RETURN(slot_number < ARRAY_LENGTH(ram_slots));
    slot = &ram_slots[slot_number];

    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH);
    psa_set_key_algorithm(&attributes, alg);
    psa_set_key_type(&attributes, slot->type);
    DRIVER_ASSERT(psa_import_key(&attributes,
                                 slot->content,
                                 PSA_BITS_TO_BYTES(slot->bits),
                                 &key) == PSA_SUCCESS);
    status = psa_sign_hash(key, alg,
                           hash, hash_length,
                           signature, signature_size, signature_length);

exit:
    psa_destroy_key(key);
    return status;
}

static psa_status_t ram_verify(psa_drv_se_context_t *context,
                               psa_key_slot_number_t slot_number,
                               psa_algorithm_t alg,
                               const uint8_t *hash,
                               size_t hash_length,
                               const uint8_t *signature,
                               size_t signature_length)
{
    ram_slot_t *slot;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT;
    psa_status_t status = PSA_ERROR_GENERIC_ERROR;

    (void) context;
    DRIVER_ASSERT_RETURN(slot_number < ARRAY_LENGTH(ram_slots));
    slot = &ram_slots[slot_number];

    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH);
    psa_set_key_algorithm(&attributes, alg);
    psa_set_key_type(&attributes, slot->type);
    DRIVER_ASSERT(psa_import_key(&attributes,
                                 slot->content,
                                 PSA_BITS_TO_BYTES(slot->bits),
                                 &key) ==
                  PSA_SUCCESS);
    status = psa_verify_hash(key, alg,
                             hash, hash_length,
                             signature, signature_length);

exit:
    psa_destroy_key(key);
    return status;
}


/****************************************************************/
/* Other test helper functions */
/****************************************************************/

typedef enum {
    SIGN_IN_SOFTWARE_AND_PARALLEL_CREATION,
    SIGN_IN_DRIVER_AND_PARALLEL_CREATION,
    SIGN_IN_DRIVER_THEN_EXPORT_PUBLIC,
} sign_verify_method_t;

/* Check that the attributes of a key reported by psa_get_key_attributes()
 * are consistent with the attributes used when creating the key. */
static int check_key_attributes(
    mbedtls_svc_key_id_t key,
    const psa_key_attributes_t *reference_attributes)
{
    int ok = 0;
    psa_key_attributes_t actual_attributes = PSA_KEY_ATTRIBUTES_INIT;

    PSA_ASSERT(psa_get_key_attributes(key, &actual_attributes));

    TEST_ASSERT(mbedtls_svc_key_id_equal(
                    psa_get_key_id(&actual_attributes),
                    psa_get_key_id(reference_attributes)));
    TEST_EQUAL(psa_get_key_lifetime(&actual_attributes),
               psa_get_key_lifetime(reference_attributes));
    TEST_EQUAL(psa_get_key_type(&actual_attributes),
               psa_get_key_type(reference_attributes));
    TEST_EQUAL(psa_get_key_usage_flags(&actual_attributes),
               psa_get_key_usage_flags(reference_attributes));
    TEST_EQUAL(psa_get_key_algorithm(&actual_attributes),
               psa_get_key_algorithm(reference_attributes));
    TEST_EQUAL(psa_get_key_enrollment_algorithm(&actual_attributes),
               psa_get_key_enrollment_algorithm(reference_attributes));
    if (psa_get_key_bits(reference_attributes) != 0) {
        TEST_EQUAL(psa_get_key_bits(&actual_attributes),
                   psa_get_key_bits(reference_attributes));
    }

    {
        psa_key_slot_number_t actual_slot_number = 0xdeadbeef;
        psa_key_slot_number_t desired_slot_number = 0xb90cc011;
        psa_key_lifetime_t lifetime =
            psa_get_key_lifetime(&actual_attributes);
        psa_status_t status = psa_get_key_slot_number(&actual_attributes,
                                                      &actual_slot_number);
        if (PSA_KEY_LIFETIME_GET_LOCATION(lifetime) < MIN_DRIVER_LOCATION) {
            /* The key is not in a secure element. */
            TEST_EQUAL(status, PSA_ERROR_INVALID_ARGUMENT);
        } else {
            /* The key is in a secure element. If it had been created
             * in a specific slot, check that it is reported there. */
            PSA_ASSERT(status);
            status = psa_get_key_slot_number(reference_attributes,
                                             &desired_slot_number);
            if (status == PSA_SUCCESS) {
                TEST_EQUAL(desired_slot_number, actual_slot_number);
            }
        }
    }
    ok = 1;

exit:
    /*
     * Actual key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&actual_attributes);

    return ok;
}

/* Get the file UID corresponding to the specified location.
 * If this changes, the storage format version must change.
 * See psa_get_se_driver_its_file_uid() in psa_crypto_se.c.
 */
psa_storage_uid_t file_uid_for_location(psa_key_location_t location)
{
    if (location > PSA_MAX_SE_LOCATION) {
        return 0;
    }
    return 0xfffffe00 + location;
}

/* Check that the persistent data of a driver has its expected content. */
static int check_persistent_data(psa_key_location_t location,
                                 const void *expected_data,
                                 size_t size)
{
    psa_storage_uid_t uid = file_uid_for_location(location);
    struct psa_storage_info_t info;
    uint8_t *loaded = NULL;
    int ok = 0;

    PSA_ASSERT(psa_its_get_info(uid, &info));
    ASSERT_ALLOC(loaded, info.size);
    PSA_ASSERT(psa_its_get(uid, 0, info.size, loaded, NULL));
    ASSERT_COMPARE(expected_data, size, loaded, info.size);
    ok = 1;

exit:
    mbedtls_free(loaded);
    return ok;
}

/* Check that no persistent data exists for the given location. */
static int check_no_persistent_data(psa_key_location_t location)
{
    psa_storage_uid_t uid = file_uid_for_location(location);
    struct psa_storage_info_t info;
    int ok = 0;

    TEST_EQUAL(psa_its_get_info(uid, &info), PSA_ERROR_DOES_NOT_EXIST);
    ok = 1;

exit:
    return ok;
}

/* Check that a function's return status is "smoke-free", i.e. that
 * it's an acceptable error code when calling an API function that operates
 * on a key with potentially bogus parameters. */
#if defined(AT_LEAST_ONE_BUILTIN_KDF)
static int is_status_smoke_free(psa_status_t status)
{
    switch (status) {
        case PSA_SUCCESS:
        case PSA_ERROR_NOT_SUPPORTED:
        case PSA_ERROR_NOT_PERMITTED:
        case PSA_ERROR_BUFFER_TOO_SMALL:
        case PSA_ERROR_INVALID_ARGUMENT:
        case PSA_ERROR_INVALID_SIGNATURE:
        case PSA_ERROR_INVALID_PADDING:
            return 1;
        default:
            return 0;
    }
}
#endif /* AT_LEAST_ONE_BUILTIN_KDF */

#define SMOKE_ASSERT(expr)                    \
    TEST_ASSERT(is_status_smoke_free(expr))

/* Smoke test a key. There are mostly no wrong answers here since we pass
 * mostly bogus parameters: the goal is to ensure that there is no memory
 * corruption or crash. This test function is most useful when run under
 * an environment with sanity checks such as ASan or MSan. */
#if defined(AT_LEAST_ONE_BUILTIN_KDF)
static int smoke_test_key(mbedtls_svc_key_id_t key)
{
    int ok = 0;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    psa_mac_operation_t mac_operation = PSA_MAC_OPERATION_INIT;
    psa_cipher_operation_t cipher_operation = PSA_CIPHER_OPERATION_INIT;
    psa_key_derivation_operation_t derivation_operation =
        PSA_KEY_DERIVATION_OPERATION_INIT;
    uint8_t buffer[80]; /* large enough for a public key for ECDH */
    size_t length;
    mbedtls_svc_key_id_t key2 = MBEDTLS_SVC_KEY_ID_INIT;

    SMOKE_ASSERT(psa_get_key_attributes(key, &attributes));

    SMOKE_ASSERT(psa_export_key(key,
                                buffer, sizeof(buffer), &length));
    SMOKE_ASSERT(psa_export_public_key(key,
                                       buffer, sizeof(buffer), &length));

    SMOKE_ASSERT(psa_copy_key(key, &attributes, &key2));
    if (!mbedtls_svc_key_id_is_null(key2)) {
        PSA_ASSERT(psa_destroy_key(key2));
    }

    SMOKE_ASSERT(psa_mac_sign_setup(&mac_operation, key, PSA_ALG_CMAC));
    PSA_ASSERT(psa_mac_abort(&mac_operation));
    SMOKE_ASSERT(psa_mac_verify_setup(&mac_operation, key,
                                      PSA_ALG_HMAC(PSA_ALG_SHA_256)));
    PSA_ASSERT(psa_mac_abort(&mac_operation));

    SMOKE_ASSERT(psa_cipher_encrypt_setup(&cipher_operation, key,
                                          PSA_ALG_CTR));
    PSA_ASSERT(psa_cipher_abort(&cipher_operation));
    SMOKE_ASSERT(psa_cipher_decrypt_setup(&cipher_operation, key,
                                          PSA_ALG_CTR));
    PSA_ASSERT(psa_cipher_abort(&cipher_operation));

    SMOKE_ASSERT(psa_aead_encrypt(key, PSA_ALG_CCM,
                                  buffer, sizeof(buffer),
                                  NULL, 0,
                                  buffer, sizeof(buffer),
                                  buffer, sizeof(buffer), &length));
    SMOKE_ASSERT(psa_aead_decrypt(key, PSA_ALG_CCM,
                                  buffer, sizeof(buffer),
                                  NULL, 0,
                                  buffer, sizeof(buffer),
                                  buffer, sizeof(buffer), &length));

    SMOKE_ASSERT(psa_sign_hash(key, PSA_ALG_ECDSA_ANY,
                               buffer, 32,
                               buffer, sizeof(buffer), &length));
    SMOKE_ASSERT(psa_verify_hash(key, PSA_ALG_ECDSA_ANY,
                                 buffer, 32,
                                 buffer, sizeof(buffer)));

    SMOKE_ASSERT(psa_asymmetric_encrypt(key, PSA_ALG_RSA_PKCS1V15_CRYPT,
                                        buffer, 10, NULL, 0,
                                        buffer, sizeof(buffer), &length));
    SMOKE_ASSERT(psa_asymmetric_decrypt(key, PSA_ALG_RSA_PKCS1V15_CRYPT,
                                        buffer, sizeof(buffer), NULL, 0,
                                        buffer, sizeof(buffer), &length));

#if defined(MBEDTLS_SHA256_C)
    /* Try the key in a plain key derivation. */
    PSA_ASSERT(psa_key_derivation_setup(&derivation_operation,
                                        PSA_ALG_HKDF(PSA_ALG_SHA_256)));
    PSA_ASSERT(psa_key_derivation_input_bytes(&derivation_operation,
                                              PSA_KEY_DERIVATION_INPUT_SALT,
                                              NULL, 0));
    SMOKE_ASSERT(psa_key_derivation_input_key(&derivation_operation,
                                              PSA_KEY_DERIVATION_INPUT_SECRET,
                                              key));
    PSA_ASSERT(psa_key_derivation_abort(&derivation_operation));

    /* If the key is asymmetric, try it in a key agreement, both as
     * part of a derivation operation and standalone. */
    if (psa_export_public_key(key, buffer, sizeof(buffer), &length) ==
        PSA_SUCCESS) {
        psa_algorithm_t alg =
            PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH,
                                  PSA_ALG_HKDF(PSA_ALG_SHA_256));
        PSA_ASSERT(psa_key_derivation_setup(&derivation_operation, alg));
        PSA_ASSERT(psa_key_derivation_input_bytes(
                       &derivation_operation, PSA_KEY_DERIVATION_INPUT_SALT,
                       NULL, 0));
        SMOKE_ASSERT(psa_key_derivation_key_agreement(
                         &derivation_operation,
                         PSA_KEY_DERIVATION_INPUT_SECRET,
                         key, buffer, length));
        PSA_ASSERT(psa_key_derivation_abort(&derivation_operation));

        SMOKE_ASSERT(psa_raw_key_agreement(
                         alg, key, buffer, length,
                         buffer, sizeof(buffer), &length));
    }
#endif /* MBEDTLS_SHA256_C */

    ok = 1;

exit:
    /*
     * Key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&attributes);

    return ok;
}
#endif /* AT_LEAST_ONE_BUILTIN_KDF */

static void psa_purge_storage(void)
{
    /* The generic code in mbedtls_test_psa_purge_key_storage()
     * (which is called by PSA_DONE()) doesn't take care of things that are
     * specific to dynamic secure elements. */
    psa_key_location_t location;
    /* Purge the transaction file. */
    psa_crypto_stop_transaction();
    /* Purge driver persistent data. */
    for (location = 0; location < PSA_MAX_SE_LOCATION; location++) {
        psa_destroy_se_persistent_data(location);
    }
}

/* END_HEADER */

/* BEGIN_DEPENDENCIES
 * depends_on:MBEDTLS_PSA_CRYPTO_SE_C
 * END_DEPENDENCIES
 */

/* BEGIN_CASE */
void register_one(int location, int version, int expected_status_arg)
{
    psa_status_t expected_status = expected_status_arg;
    psa_drv_se_t driver;

    memset(&driver, 0, sizeof(driver));
    driver.hal_version = version;

    TEST_EQUAL(psa_register_se_driver(location, &driver),
               expected_status);

    PSA_ASSERT(psa_crypto_init());

exit:
    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE */
void register_twice(int count)
{
    psa_drv_se_t driver;
    psa_key_location_t location;
    psa_key_location_t max = MIN_DRIVER_LOCATION + count;

    memset(&driver, 0, sizeof(driver));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;

    for (location = MIN_DRIVER_LOCATION; location < max; location++) {
        PSA_ASSERT(psa_register_se_driver(location, &driver));
    }
    for (location = MIN_DRIVER_LOCATION; location < max; location++) {
        TEST_EQUAL(psa_register_se_driver(location, &driver),
                   PSA_ERROR_ALREADY_EXISTS);
    }

    PSA_ASSERT(psa_crypto_init());

exit:
    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE */
void register_max()
{
    psa_drv_se_t driver;
    psa_key_location_t location;
    psa_key_location_t max = MIN_DRIVER_LOCATION + PSA_MAX_SE_DRIVERS;

    memset(&driver, 0, sizeof(driver));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;

    for (location = MIN_DRIVER_LOCATION; location < max; location++) {
        PSA_ASSERT(psa_register_se_driver(location, &driver));
    }

    TEST_EQUAL(psa_register_se_driver(location, &driver),
               PSA_ERROR_INSUFFICIENT_MEMORY);

    PSA_ASSERT(psa_crypto_init());

exit:
    PSA_DONE();
}
/* END_CASE */

/* BEGIN_CASE */
void key_creation_import_export(int lifetime_arg, int min_slot, int restart)
{
    psa_drv_se_t driver;
    psa_drv_se_key_management_t key_management;
    psa_key_lifetime_t lifetime = (psa_key_lifetime_t) lifetime_arg;
    psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(1, 1);
    mbedtls_svc_key_id_t returned_id = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_handle_t handle;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    const uint8_t key_material[3] = { 0xfa, 0xca, 0xde };
    uint8_t exported[sizeof(key_material)];
    size_t exported_length;

    TEST_USES_KEY_ID(id);

    memset(&driver, 0, sizeof(driver));
    memset(&key_management, 0, sizeof(key_management));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;
    driver.key_management = &key_management;
    driver.persistent_data_size = sizeof(ram_slot_usage_t);
    key_management.p_allocate = ram_allocate;
    key_management.p_import = ram_import;
    key_management.p_destroy = ram_destroy;
    key_management.p_export = ram_export;
    ram_min_slot = min_slot;

    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());

    /* Create a key. */
    psa_set_key_id(&attributes, id);
    psa_set_key_lifetime(&attributes, lifetime);
    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
    psa_set_key_type(&attributes, PSA_KEY_TYPE_RAW_DATA);
    PSA_ASSERT(psa_import_key(&attributes,
                              key_material, sizeof(key_material),
                              &returned_id));

    if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
        /* For volatile keys, check no persistent data was created */
        if (!check_no_persistent_data(location)) {
            goto exit;
        }
    } else {
        /* For persistent keys, check persistent data */
        if (!check_persistent_data(location,
                                   &ram_shadow_slot_usage,
                                   sizeof(ram_shadow_slot_usage))) {
            goto exit;
        }
    }

    /* Test that the key was created in the expected slot. */
    TEST_EQUAL(ram_slots[min_slot].type, PSA_KEY_TYPE_RAW_DATA);

    /* Maybe restart, to check that the information is saved correctly. */
    if (restart) {
        mbedtls_psa_crypto_free();
        PSA_ASSERT(psa_register_se_driver(location, &driver));
        PSA_ASSERT(psa_crypto_init());

        if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
            /* Check that the PSA core has no knowledge of the volatile key */
            TEST_ASSERT(psa_open_key(returned_id, &handle) ==
                        PSA_ERROR_DOES_NOT_EXIST);

            /* Drop data from our mockup driver */
            ram_slots_reset();
            ram_min_slot = min_slot;

            /* Re-import key */
            PSA_ASSERT(psa_import_key(&attributes,
                                      key_material, sizeof(key_material),
                                      &returned_id));
        } else {
            /* Check the persistent key file */
            if (!check_persistent_data(location,
                                       &ram_shadow_slot_usage,
                                       sizeof(ram_shadow_slot_usage))) {
                goto exit;
            }
        }
    }

    /* Test that the key was created in the expected slot. */
    TEST_EQUAL(ram_slots[min_slot].type, PSA_KEY_TYPE_RAW_DATA);

    /* Test the key attributes, including the reported slot number. */
    psa_set_key_bits(&attributes,
                     PSA_BYTES_TO_BITS(sizeof(key_material)));
    psa_set_key_slot_number(&attributes, min_slot);

    if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
        attributes.core.id = returned_id;
    } else {
        psa_set_key_id(&attributes, returned_id);
    }

    if (!check_key_attributes(returned_id, &attributes)) {
        goto exit;
    }

    /* Test the key data. */
    PSA_ASSERT(psa_export_key(returned_id,
                              exported, sizeof(exported),
                              &exported_length));
    ASSERT_COMPARE(key_material, sizeof(key_material),
                   exported, exported_length);

    PSA_ASSERT(psa_destroy_key(returned_id));
    if (!check_persistent_data(location,
                               &ram_shadow_slot_usage,
                               sizeof(ram_shadow_slot_usage))) {
        goto exit;
    }
    TEST_EQUAL(psa_open_key(returned_id, &handle),
               PSA_ERROR_DOES_NOT_EXIST);

    /* Test that the key has been erased from the designated slot. */
    TEST_EQUAL(ram_slots[min_slot].type, 0);

exit:
    PSA_DONE();
    ram_slots_reset();
    psa_purge_storage();
}
/* END_CASE */

/* BEGIN_CASE */
void key_creation_in_chosen_slot(int slot_arg,
                                 int restart,
                                 int expected_status_arg)
{
    psa_key_slot_number_t wanted_slot = slot_arg;
    psa_status_t expected_status = expected_status_arg;
    psa_status_t status;
    psa_drv_se_t driver;
    psa_drv_se_key_management_t key_management;
    psa_key_lifetime_t lifetime = TEST_SE_PERSISTENT_LIFETIME;
    psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(1, 1);
    mbedtls_svc_key_id_t returned_id;
    psa_key_handle_t handle;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    const uint8_t key_material[3] = { 0xfa, 0xca, 0xde };

    TEST_USES_KEY_ID(id);

    memset(&driver, 0, sizeof(driver));
    memset(&key_management, 0, sizeof(key_management));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;
    driver.key_management = &key_management;
    driver.persistent_data_size = sizeof(ram_slot_usage_t);
    key_management.p_validate_slot_number = ram_validate_slot_number;
    key_management.p_import = ram_import;
    key_management.p_destroy = ram_destroy;
    key_management.p_export = ram_export;

    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());

    /* Create a key. */
    psa_set_key_id(&attributes, id);
    psa_set_key_lifetime(&attributes, lifetime);
    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
    psa_set_key_type(&attributes, PSA_KEY_TYPE_RAW_DATA);
    psa_set_key_slot_number(&attributes, wanted_slot);
    status = psa_import_key(&attributes,
                            key_material, sizeof(key_material),
                            &returned_id);
    TEST_EQUAL(status, expected_status);

    if (status != PSA_SUCCESS) {
        goto exit;
    }
    if (!check_persistent_data(location,
                               &ram_shadow_slot_usage,
                               sizeof(ram_shadow_slot_usage))) {
        goto exit;
    }

    /* Maybe restart, to check that the information is saved correctly. */
    if (restart) {
        mbedtls_psa_crypto_free();
        PSA_ASSERT(psa_register_se_driver(location, &driver));
        PSA_ASSERT(psa_crypto_init());
        if (!check_persistent_data(location,
                                   &ram_shadow_slot_usage,
                                   sizeof(ram_shadow_slot_usage))) {
            goto exit;
        }
    }

    /* Test that the key was created in the expected slot. */
    TEST_EQUAL(ram_slots[wanted_slot].type, PSA_KEY_TYPE_RAW_DATA);

    /* Test that the key is reported with the correct attributes,
     * including the expected slot. */
    PSA_ASSERT(psa_get_key_attributes(id, &attributes));

    PSA_ASSERT(psa_destroy_key(id));
    if (!check_persistent_data(location,
                               &ram_shadow_slot_usage,
                               sizeof(ram_shadow_slot_usage))) {
        goto exit;
    }
    TEST_EQUAL(psa_open_key(id, &handle), PSA_ERROR_DOES_NOT_EXIST);

exit:
    /*
     * Key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&attributes);

    PSA_DONE();
    ram_slots_reset();
    psa_purge_storage();
}
/* END_CASE */

/* BEGIN_CASE depends_on:AT_LEAST_ONE_BUILTIN_KDF */
void import_key_smoke(int type_arg, int alg_arg,
                      data_t *key_material)
{
    psa_key_type_t type = type_arg;
    psa_algorithm_t alg = alg_arg;
    psa_drv_se_t driver;
    psa_drv_se_key_management_t key_management;
    psa_key_lifetime_t lifetime = TEST_SE_PERSISTENT_LIFETIME;
    psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(1, 1);
    mbedtls_svc_key_id_t returned_id;
    psa_key_handle_t handle;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;

    TEST_USES_KEY_ID(id);

    memset(&driver, 0, sizeof(driver));
    memset(&key_management, 0, sizeof(key_management));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;
    driver.key_management = &key_management;
    driver.persistent_data_size = sizeof(psa_key_slot_number_t);
    key_management.p_allocate = counter_allocate;
    key_management.p_import = null_import;
    key_management.p_destroy = null_destroy;

    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());

    /* Create a key. */
    psa_set_key_id(&attributes, id);
    psa_set_key_lifetime(&attributes, lifetime);
    psa_set_key_usage_flags(&attributes,
                            PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH |
                            PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT |
                            PSA_KEY_USAGE_EXPORT);
    psa_set_key_algorithm(&attributes, alg);
    psa_set_key_type(&attributes, type);
    PSA_ASSERT(psa_import_key(&attributes,
                              key_material->x, key_material->len,
                              &returned_id));
    if (!check_persistent_data(location,
                               &shadow_counter, sizeof(shadow_counter))) {
        goto exit;
    }

    /* Do stuff with the key. */
    if (!smoke_test_key(id)) {
        goto exit;
    }

    /* Restart and try again. */
    mbedtls_psa_crypto_free();
    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());
    if (!check_persistent_data(location,
                               &shadow_counter, sizeof(shadow_counter))) {
        goto exit;
    }
    if (!smoke_test_key(id)) {
        goto exit;
    }

    /* We're done. */
    PSA_ASSERT(psa_destroy_key(id));
    if (!check_persistent_data(location,
                               &shadow_counter, sizeof(shadow_counter))) {
        goto exit;
    }
    TEST_EQUAL(psa_open_key(id, &handle), PSA_ERROR_DOES_NOT_EXIST);

exit:
    PSA_DONE();
    counter_reset();
    psa_purge_storage();
}
/* END_CASE */

/* BEGIN_CASE */
void generate_key_not_supported(int type_arg, int bits_arg)
{
    psa_key_type_t type = type_arg;
    size_t bits = bits_arg;
    psa_drv_se_t driver;
    psa_drv_se_key_management_t key_management;
    psa_key_lifetime_t lifetime = TEST_SE_PERSISTENT_LIFETIME;
    psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(1, 1);
    mbedtls_svc_key_id_t returned_id;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;

    TEST_USES_KEY_ID(id);

    memset(&driver, 0, sizeof(driver));
    memset(&key_management, 0, sizeof(key_management));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;
    driver.key_management = &key_management;
    driver.persistent_data_size = sizeof(psa_key_slot_number_t);
    key_management.p_allocate = counter_allocate;
    /* No p_generate method */

    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());

    psa_set_key_id(&attributes, id);
    psa_set_key_lifetime(&attributes, lifetime);
    psa_set_key_type(&attributes, type);
    psa_set_key_bits(&attributes, bits);
    TEST_EQUAL(psa_generate_key(&attributes, &returned_id),
               PSA_ERROR_NOT_SUPPORTED);

exit:
    PSA_DONE();
    counter_reset();
    psa_purge_storage();
}
/* END_CASE */

/* BEGIN_CASE depends_on:AT_LEAST_ONE_BUILTIN_KDF */
void generate_key_smoke(int type_arg, int bits_arg, int alg_arg)
{
    psa_key_type_t type = type_arg;
    psa_key_bits_t bits = bits_arg;
    psa_algorithm_t alg = alg_arg;
    psa_drv_se_t driver;
    psa_drv_se_key_management_t key_management;
    psa_key_lifetime_t lifetime = TEST_SE_PERSISTENT_LIFETIME;
    psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(1, 1);
    mbedtls_svc_key_id_t returned_id;
    psa_key_handle_t handle;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;

    TEST_USES_KEY_ID(id);

    memset(&driver, 0, sizeof(driver));
    memset(&key_management, 0, sizeof(key_management));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;
    driver.key_management = &key_management;
    driver.persistent_data_size = sizeof(psa_key_slot_number_t);
    key_management.p_allocate = counter_allocate;
    key_management.p_generate = null_generate;
    key_management.p_destroy = null_destroy;

    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());

    /* Create a key. */
    psa_set_key_id(&attributes, id);
    psa_set_key_lifetime(&attributes, lifetime);
    psa_set_key_usage_flags(&attributes,
                            PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH |
                            PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT |
                            PSA_KEY_USAGE_EXPORT);
    psa_set_key_algorithm(&attributes, alg);
    psa_set_key_type(&attributes, type);
    psa_set_key_bits(&attributes, bits);
    PSA_ASSERT(psa_generate_key(&attributes, &returned_id));
    if (!check_persistent_data(location,
                               &shadow_counter, sizeof(shadow_counter))) {
        goto exit;
    }

    /* Do stuff with the key. */
    if (!smoke_test_key(id)) {
        goto exit;
    }

    /* Restart and try again. */
    mbedtls_psa_crypto_free();
    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());
    if (!check_persistent_data(location,
                               &shadow_counter, sizeof(shadow_counter))) {
        goto exit;
    }
    if (!smoke_test_key(id)) {
        goto exit;
    }

    /* We're done. */
    PSA_ASSERT(psa_destroy_key(id));
    if (!check_persistent_data(location,
                               &shadow_counter, sizeof(shadow_counter))) {
        goto exit;
    }
    TEST_EQUAL(psa_open_key(id, &handle), PSA_ERROR_DOES_NOT_EXIST);

exit:
    PSA_DONE();
    counter_reset();
    psa_purge_storage();
}
/* END_CASE */

/* BEGIN_CASE */
void sign_verify(int flow,
                 int type_arg, int alg_arg,
                 int bits_arg, data_t *key_material,
                 data_t *input)
{
    psa_key_type_t type = type_arg;
    psa_algorithm_t alg = alg_arg;
    size_t bits = bits_arg;
    /* Pass bits=0 to import, bits>0 to fake-generate */
    int generating = (bits != 0);

    psa_drv_se_t driver;
    psa_drv_se_key_management_t key_management;
    psa_drv_se_asymmetric_t asymmetric;

    psa_key_lifetime_t lifetime = TEST_SE_PERSISTENT_LIFETIME;
    psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(1, 1);
    mbedtls_svc_key_id_t returned_id;
    mbedtls_svc_key_id_t sw_key = MBEDTLS_SVC_KEY_ID_INIT;
    psa_key_attributes_t sw_attributes = PSA_KEY_ATTRIBUTES_INIT;
    psa_key_attributes_t drv_attributes;
    uint8_t signature[PSA_SIGNATURE_MAX_SIZE];
    size_t signature_length;

    TEST_USES_KEY_ID(id);

    memset(&driver, 0, sizeof(driver));
    memset(&key_management, 0, sizeof(key_management));
    memset(&asymmetric, 0, sizeof(asymmetric));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;
    driver.key_management = &key_management;
    driver.asymmetric = &asymmetric;
    driver.persistent_data_size = sizeof(ram_slot_usage_t);
    key_management.p_allocate = ram_allocate;
    key_management.p_destroy = ram_destroy;
    if (generating) {
        key_management.p_generate = ram_fake_generate;
    } else {
        key_management.p_import = ram_import;
    }
    switch (flow) {
        case SIGN_IN_SOFTWARE_AND_PARALLEL_CREATION:
            break;
        case SIGN_IN_DRIVER_AND_PARALLEL_CREATION:
            asymmetric.p_sign = ram_sign;
            break;
        case SIGN_IN_DRIVER_THEN_EXPORT_PUBLIC:
            asymmetric.p_sign = ram_sign;
            key_management.p_export_public = ram_export_public;
            break;
        default:
            TEST_ASSERT(!"unsupported flow (should be SIGN_IN_xxx)");
            break;
    }
    asymmetric.p_verify = ram_verify;

    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());

    /* Prepare to create two keys with the same key material: a transparent
     * key, and one that goes through the driver. */
    psa_set_key_usage_flags(&sw_attributes,
                            PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
    psa_set_key_algorithm(&sw_attributes, alg);
    psa_set_key_type(&sw_attributes, type);
    drv_attributes = sw_attributes;
    psa_set_key_id(&drv_attributes, id);
    psa_set_key_lifetime(&drv_attributes, lifetime);

    /* Create the key in the driver. */
    if (generating) {
        psa_set_key_bits(&drv_attributes, bits);
        PSA_ASSERT(psa_generate_key(&drv_attributes, &returned_id));
        /* Since we called a generate method that does not actually
         * generate material, store the desired result of generation in
         * the mock secure element storage. */
        PSA_ASSERT(psa_get_key_attributes(id, &drv_attributes));
        TEST_EQUAL(key_material->len, PSA_BITS_TO_BYTES(bits));
        memcpy(ram_slots[ram_min_slot].content, key_material->x,
               key_material->len);
    } else {
        PSA_ASSERT(psa_import_key(&drv_attributes,
                                  key_material->x, key_material->len,
                                  &returned_id));
    }

    /* Either import the same key in software, or export the driver's
     * public key and import that. */
    switch (flow) {
        case SIGN_IN_SOFTWARE_AND_PARALLEL_CREATION:
        case SIGN_IN_DRIVER_AND_PARALLEL_CREATION:
            PSA_ASSERT(psa_import_key(&sw_attributes,
                                      key_material->x, key_material->len,
                                      &sw_key));
            break;
        case SIGN_IN_DRIVER_THEN_EXPORT_PUBLIC:
        {
            uint8_t public_key[PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS)
            ];
            size_t public_key_length;
            PSA_ASSERT(psa_export_public_key(id,
                                             public_key, sizeof(public_key),
                                             &public_key_length));
            psa_set_key_type(&sw_attributes,
                             PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type));
            PSA_ASSERT(psa_import_key(&sw_attributes,
                                      public_key, public_key_length,
                                      &sw_key));
            break;
        }
    }

    /* Sign with the chosen key. */
    switch (flow) {
        case SIGN_IN_DRIVER_AND_PARALLEL_CREATION:
        case SIGN_IN_DRIVER_THEN_EXPORT_PUBLIC:
            PSA_ASSERT_VIA_DRIVER(
                psa_sign_hash(id, alg,
                              input->x, input->len,
                              signature, sizeof(signature),
                              &signature_length),
                PSA_SUCCESS);
            break;
        case SIGN_IN_SOFTWARE_AND_PARALLEL_CREATION:
            PSA_ASSERT(psa_sign_hash(sw_key, alg,
                                     input->x, input->len,
                                     signature, sizeof(signature),
                                     &signature_length));
            break;
    }

    /* Verify with both keys. */
    PSA_ASSERT(psa_verify_hash(sw_key, alg,
                               input->x, input->len,
                               signature, signature_length));
    PSA_ASSERT_VIA_DRIVER(
        psa_verify_hash(id, alg,
                        input->x, input->len,
                        signature, signature_length),
        PSA_SUCCESS);

    /* Change the signature and verify again. */
    signature[0] ^= 1;
    TEST_EQUAL(psa_verify_hash(sw_key, alg,
                               input->x, input->len,
                               signature, signature_length),
               PSA_ERROR_INVALID_SIGNATURE);
    PSA_ASSERT_VIA_DRIVER(
        psa_verify_hash(id, alg,
                        input->x, input->len,
                        signature, signature_length),
        PSA_ERROR_INVALID_SIGNATURE);

exit:
    /*
     * Driver key attributes may have been returned by psa_get_key_attributes()
     * thus reset them as required.
     */
    psa_reset_key_attributes(&drv_attributes);

    psa_destroy_key(id);
    psa_destroy_key(sw_key);
    PSA_DONE();
    ram_slots_reset();
    psa_purge_storage();
}
/* END_CASE */

/* BEGIN_CASE */
void register_key_smoke_test(int lifetime_arg,
                             int owner_id_arg,
                             int id_arg,
                             int validate,
                             int expected_status_arg)
{
    psa_key_lifetime_t lifetime = lifetime_arg;
    psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
    psa_status_t expected_status = expected_status_arg;
    psa_drv_se_t driver;
    psa_drv_se_key_management_t key_management;
    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
    mbedtls_svc_key_id_t id = mbedtls_svc_key_id_make(owner_id_arg, id_arg);
    psa_key_handle_t handle;
    size_t bit_size = 48;
    psa_key_slot_number_t wanted_slot = 0x123456789;
    psa_status_t status;

    TEST_USES_KEY_ID(id);

    memset(&driver, 0, sizeof(driver));
    driver.hal_version = PSA_DRV_SE_HAL_VERSION;
    memset(&key_management, 0, sizeof(key_management));
    driver.key_management = &key_management;
    key_management.p_destroy = null_destroy;
    if (validate >= 0) {
        key_management.p_validate_slot_number = validate_slot_number_as_directed;
        validate_slot_number_directions.slot_number = wanted_slot;
        validate_slot_number_directions.method = PSA_KEY_CREATION_REGISTER;
        validate_slot_number_directions.status =
            (validate > 0 ? PSA_SUCCESS : PSA_ERROR_NOT_PERMITTED);
    }

    mbedtls_test_set_step(1);
    PSA_ASSERT(psa_register_se_driver(MIN_DRIVER_LOCATION, &driver));
    PSA_ASSERT(psa_crypto_init());

    psa_set_key_id(&attributes, id);
    psa_set_key_lifetime(&attributes, lifetime);
    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
    psa_set_key_type(&attributes, PSA_KEY_TYPE_RAW_DATA);
    psa_set_key_bits(&attributes, bit_size);
    psa_set_key_slot_number(&attributes, wanted_slot);

    status = mbedtls_psa_register_se_key(&attributes);
    TEST_EQUAL(status, expected_status);

    if (status != PSA_SUCCESS) {
        goto exit;
    }

    /* Test that the key exists and has the expected attributes. */
    if (!check_key_attributes(id, &attributes)) {
        goto exit;
    }

#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
    mbedtls_svc_key_id_t invalid_id =
        mbedtls_svc_key_id_make(owner_id_arg + 1, id_arg);
    TEST_EQUAL(psa_open_key(invalid_id, &handle), PSA_ERROR_DOES_NOT_EXIST);
#endif

    PSA_ASSERT(psa_purge_key(id));

    /* Restart and try again. */
    mbedtls_test_set_step(2);
    PSA_SESSION_DONE();
    PSA_ASSERT(psa_register_se_driver(location, &driver));
    PSA_ASSERT(psa_crypto_init());
    if (!check_key_attributes(id, &attributes)) {
        goto exit;
    }
    /* This time, destroy the key. */
    PSA_ASSERT(psa_destroy_key(id));
    TEST_EQUAL(psa_open_key(id, &handle), PSA_ERROR_DOES_NOT_EXIST);

exit:
    psa_reset_key_attributes(&attributes);
    psa_destroy_key(id);
    PSA_DONE();
    psa_purge_storage();
    memset(&validate_slot_number_directions, 0,
           sizeof(validate_slot_number_directions));
}
/* END_CASE */
